package com.hong.redis.redisex;

import com.google.gson.Gson;
import redis.clients.jedis.Jedis;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class RedisUtil {

    private static Jedis jedis = new Jedis("localhost",6379);
    private static Gson gson = new Gson();
    // 如果未设置时间默认30分钟
    private static final long DEFAULT_EXPIRE_TIME = 30 * 60 * 1000;


    /**
     * 将元素存放到指定的list集合中
     * @param listKey  存放的集合名称
     * @param expireTime  元素过期时间
     * @param key 集合中的元素值
     */
    public static void setVal2List(String listKey, long expireTime , String ... key){
        int length = key.length;
        for(int i = 0 ;i < length ; i ++){
            Entry entry = new Entry(key[i],System.currentTimeMillis() + expireTime);
            jedis.rpush(listKey,gson.toJson(entry));
        }
    }

    public static void setVal2List(String listKey, String ... key){
        int length = key.length;
        for(int i = 0 ;i < length ; i ++){
            Entry entry = new Entry(key[i],System.currentTimeMillis() + DEFAULT_EXPIRE_TIME);
            jedis.rpush(listKey,gson.toJson(entry));
        }
    }


    /**
     * 根据下标获取集合中的元素
     * 若集合中的元素已经过期，则删除，否则返回给用户
     * @param listKey 存放的集合名称
     * @param index 用户传入的下标
     * @return 返回指定的key，或者为空字符串
     */
    public static String getValFromListByIndex(String listKey, long index){
       long length = getLength(listKey);
       if(index < 0 || index >= length) {
           return "";
       }
       String json = jedis.lindex(listKey, index);
       Entry entry = gson.fromJson(json, Entry.class);
       if(validateExpire(entry.getExpireTime())){
           return entry.getKey();
       }
       // 删除过期元素
       jedis.lrem(listKey,1,json);
       return "";
    }

    /**
     * 封装Redis中指定范围获取多个元素
     * @param listKey 集合名称
     * @param start 起始下标
     * @param end    结束下标
     * @return  返回该范围内的所有值
     */
    public static ArrayList<String> getRange(String listKey, int start , int end){
        // 获取该范围内的所有value值
        List<String> JSONList = jedis.lrange(listKey, start, end);

        // 将每个JSON对象映射为指定的Entry对象
        List<Entry> entries = JSONList.stream().map(v1 -> {
            return gson.fromJson(v1, Entry.class);
        }).collect(Collectors.toList());


        ArrayList<String> res = new ArrayList<>();
        for(int i = 0 ; i < entries.size() ;  i ++){
            if(validateExpire(entries.get(i).getExpireTime())){
                res.add(entries.get(i).getKey());
            }else{
                jedis.lrem(listKey,1,JSONList.get(i));
            }
        }
        return res;
    }

    private static ArrayList<Entry> getRange0(String listKey, int start , int end){
        // 获取该范围内的所有value值
        List<String> JSONList = jedis.lrange(listKey, start, end);

        // 将每个JSON对象映射为指定的Entry对象
        List<Entry> entries = JSONList.stream().map(v1 -> {
            return gson.fromJson(v1, Entry.class);
        }).collect(Collectors.toList());


        ArrayList<Entry> res = new ArrayList<>();
        for(int i = 0 ; i < entries.size() ;  i ++){
            if(validateExpire(entries.get(i).getExpireTime())){
                res.add(entries.get(i));
            }else{
                jedis.lrem(listKey,1,JSONList.get(i));
            }
        }
        return res;
    }

    /**
     * 遍历整个集合，删除所有过期元素
     * @param listKey
     */
    private static void removeALLExpireTime(String listKey){
      getRange(listKey,0,-1);
    }


    /**
     *  将指定索引的值设置为新值
     */
    public static boolean setNewValByIndex(String listKey, long index,String newKey){
        removeALLExpireTime(listKey);
        // 下标合法
        if(validIndex(listKey,index)){
            String json = jedis.lindex(listKey, index);
            Entry entry = gson.fromJson(json, Entry.class);
            entry.setKey(newKey);
            jedis.lset(listKey,index,gson.toJson(entry));
            return true;
        }
        return false;

    }

    /**
     * 修改过期时间
     */
    public static boolean setNewValByIndex(String listKey, long index,long newExpireTime){
        removeALLExpireTime(listKey);
        // 下标合法
        if(validIndex(listKey,index)){
            String json = jedis.lindex(listKey, index);
            Entry entry = gson.fromJson(json, Entry.class);
            // 当前时间加上过期时间
            entry.setExpireTime(System.currentTimeMillis() + newExpireTime);
            jedis.lset(listKey,index,gson.toJson(entry));
            return true;
        }
        return false;

    }

    /**
     * 指定value删除所有过期元素，可能会删除多个元素
     * @param listKey
     * @param key
     */
    public static void removeVal(String listKey , String key){
        ArrayList<Entry> entries = getRange0(listKey, 0, -1);
        for(int i = 0 ; i < entries.size() ; i ++){
            if(entries.get(i).getKey().equals(key)){
                jedis.lrem(listKey,1,gson.toJson(entries.get(i)));
            }
        }
    }

    /**
     * 修改value  和 过期时间
     */
    public static boolean setNewValByIndex(String listKey, long index,String newKey, long newExpireTime){
        removeALLExpireTime(listKey);
        // 下标合法
        if(validIndex(listKey,index)){
            // 如果有元素未过期，则可以进行操作，但是如果没有设置过期时间，则需要删除元素，重新设置
            String json = jedis.lindex(listKey, index);
            Entry entry = gson.fromJson(json, Entry.class);
            entry.setKey(newKey);
            entry.setExpireTime(System.currentTimeMillis() + newExpireTime);
            jedis.lset(listKey,index,gson.toJson(entry));
            return true;
        }
        return false;

    }

    /**
     * 验证下标是否过期
     * @param listKey
     * @param index
     * @return
     */
    private static boolean validIndex(String listKey , long index){
        long length = getLength(listKey);
        if(index < 0  || index >= length){
            return false;
        }
        return true;
    }
    /**
     * 获取集合中的元素个数
     * @param listKey
     * @return
     */
    private static long getLength(String listKey){
        return jedis.llen(listKey);
    }
    /**
     * 判断元素是否已经过期
     * @param time 存活截止时间
     * @return
     */
    private static boolean validateExpire(long time){
        return time >= System.currentTimeMillis();
    }

}
